package shenxg.game;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;

import javax.imageio.ImageIO;

import shenxg.GameMain;

public class Scene {
	public static final int X_LENGTH = 10;
	public static final int Y_LENGTH = 10;
	public static int img_read_begain_x = 44;
	public static int img_read_begain_y = 223;
	public static int img_read_width = 68;

	public static Scene bestFinalScene = null;
	public static Scene finalAmountScene = null;

	public static HashSet<Scene> allFinishedSceneSet;

	public Item[][] items = new Item[X_LENGTH][Y_LENGTH];
	public int itemsLeftAmount = 0;
	public Scene sceneParent;
	public final ItemGroup parentDestoryItemGroup;
	public Scene bestScoreChild = null;
	// private ArrayList<Scene> sceneChildren = new ArrayList<Scene>();
	private ArrayList<ItemGroup> itemGroups = new ArrayList<ItemGroup>();

	private boolean isFinal = false;
	private boolean isCalculateFinish = false;
	private int scoreNow = 0;

	public boolean equals(Object o) {
		Scene scene = null;
		if (!(o instanceof Scene)) {
			return false;
		}

		scene = (Scene) o;
		if (this.hashCode() != scene.hashCode())
			return false;

		for (int x = 0; x < items.length; x++) {
			for (int y = 0; y < items[x].length; y++) {
				if ((items[x][y] != null && scene.items[x][y] == null) || (items[x][y] == null && scene.items[x][y] != null))
					return false;
				if ((items[x][y] != null && scene.items[x][y] != null) && (items[x][y].getColorType() != scene.items[x][y].getColorType()))
					return false;
			}
		}
		return true;
	}

	public int hashCode() {
		return 100 * this.getItemsAmount() + itemGroups.size();
	}

	public Scene(File file) throws Exception {
		sceneParent = null;
		parentDestoryItemGroup = null;
		initFromImgeFile(file);
		Collections.sort(this.getItemGroups());
	}

	public Scene(Scene sceneParent, ItemGroup itemGroup) {
		parentDestoryItemGroup = itemGroup;
		this.sceneParent = sceneParent;
		this.scoreNow = sceneParent.scoreNow;

		// colne items
		for (int i = 0; i < items.length; i++) {
			for (int j = 0; j < items[i].length; j++) {
				if (null == sceneParent.items[i][j]) {
					this.items[i][j] = null;
				} else {
					this.items[i][j] = sceneParent.items[i][j].clone(this);
				}
			}
		}
	}

	public Scene getBestFinalChild() {
		if (this.isFinal) {
			return this;
		}

		if (null == bestScoreChild) {
			return null;
		}

		return this.bestScoreChild.getBestFinalChild();
	}

	public void calculateBySameScene(Scene sameScene) throws Exception {
		if (sameScene.isFinal)
			return;
		ItemGroup destoryItemGroup = sameScene.bestScoreChild.parentDestoryItemGroup;
		boolean find = false;
		for (ItemGroup group : this.itemGroups) {
			if (group.equals(destoryItemGroup)) {
				find = true;
				Scene childScene = group.destroy();
				this.bestScoreChild = childScene;
				childScene.calculateBySameScene(sameScene.bestScoreChild);
				return;
			}
		}
		if (!find) {
			throw new Exception("not find same ItemGroup!!!!");
		}
	}

	public void calculate() throws Exception {
		if (this.sceneParent == null) {
			allFinishedSceneSet = new HashSet<Scene>();
		}

		if (0 == itemGroups.size()) {
			isFinal = true;
			isCalculateFinish = true;
			return;
		}

		if (GameStart.useSet && this.itemGroups.size() > GameStart.smallstSet) {
			if (allFinishedSceneSet.contains(this)) {
				isCalculateFinish = true;
				for (Scene sceneInSet : allFinishedSceneSet) {
					if (sceneInSet.equals(this)) {
						this.calculateBySameScene(sceneInSet);
						return;
					}
				}
				throw new Exception("not find same Scene!!!!");
			}
		}

		int bestFinalChildScore = 0;

		for (ItemGroup itemGroup : itemGroups) {
			Scene scene = itemGroup.destroy();
			scene.calculate();

			if (this.bestScoreChild == null) {
				this.bestScoreChild = scene;
			}

			Scene bestFinalchild = scene.getBestFinalChild();

			if (bestFinalchild.getFinalScore() > bestFinalChildScore) {
				this.bestScoreChild = scene;
			}
		}

		isCalculateFinish = true;
		if (GameStart.useSet && this.itemGroups.size() > GameStart.smallstSet) {
			allFinishedSceneSet.add(this);
		}
	}

	public ArrayList<ItemGroup> getItemGroups() {
		return itemGroups;
	}

	public void initFromImgeFile(File file) throws Exception {
		BufferedImage image;
		try {
			image = ImageIO.read(file);
			Graphics g = image.getGraphics();
			for (int y = 0; y < Y_LENGTH; y++) {
				for (int x = 0; x < X_LENGTH; x++) {
					Color color = new Color(image.getRGB(img_read_begain_x + img_read_width * x + img_read_width / 2, img_read_begain_y
							+ img_read_width * y + img_read_width / 2));
					items[x][y] = new Item(x, y, color, this);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		initItemGroups();
	}

	public int getScoreFromItemGroups() {
		int score = 0;
		for (ItemGroup itemGroup : itemGroups) {
			score += itemGroup.getScore();
		}
		return score;
	}

	public void initItemGroups() throws Exception {
		itemGroups = new ArrayList<ItemGroup>();
		Item[][] items = new Item[X_LENGTH][Y_LENGTH];
		// copy (not clone)
		for (int i = 0; i < items.length; i++) {
			for (int j = 0; j < items[i].length; j++) {
				items[i][j] = this.items[i][j];
			}
		}

		for (int x = 0; x < items.length; x++) {
			for (int y = 0; y < items[x].length; y++) {
				if (items[x][y] != null) {
					ArrayList<Item> nearestItems = items[x][y].getNearestSameColorItems();
					if (nearestItems.size() != 0) {
						ItemGroup itemGroup = new ItemGroup(this, items[x][y].getColorType());
						itemGroup.addItem(items[x][y]);
						for (Item item : nearestItems) {
							itemGroup.addItem(item);
						}
						int i = 1;
						while (i < itemGroup.items.size()) {
							for (Item item : itemGroup.items.get(i).getNearestSameColorItems()) {
								itemGroup.addItem(item);
							}
							i++;
						}

						for (Item item : itemGroup.items) {
							items[item.getX()][item.getY()] = null;
						}

						itemGroups.add(itemGroup);
					}
				}
			}
		}

		if (this.itemGroups.size() == 0) {
			this.isFinal = true;
		}
	}

	public int getItemsAmount() {
		if (itemsLeftAmount > 0) {
			return itemsLeftAmount;
		}
		int amount = 0;
		for (Item[] items : this.items) {
			for (Item item : items) {
				if (null != item) {
					amount++;
				}
			}
		}
		itemsLeftAmount = amount;
		return amount;
	}

	public int getItemsAmountScore() {
		int amount = getItemsAmount();
		switch (amount) {
		case 0:
			return 2000;
		case 1:
			return 1980;
		case 2:
			return 1920;
		case 3:
			return 1820;
		case 4:
			return 1680;
		case 5:
			return 1500;
		case 6:
			return 1280;
		case 7:
			return 1020;
		case 8:
			return 720;
		case 9:
			return 380;
		default:
			return 0;

		}
	}

	public int getFinalScore() {
		return this.scoreNow + getItemsAmountScore();
	}

	public void addScoreNow(int scoreNow) {
		this.scoreNow += scoreNow;
	}

	public boolean isFinal() {
		return isFinal;
	}

	public int getScoreNow() {
		return scoreNow;
	}

	public int getScorePotential() {
		return this.getScoreNow() + this.getScoreFromItemGroups();
	}

	/*
	 * public int getItemsAmount() { int amount = 0; for (int x = 0; x <
	 * items.length; x++) { for (int y = 0; y < items[x].length; y++) { if (null
	 * != items[x][y]) { amount++; } } } return amount; }
	 */
	// item has nulls to dell with
	public void reInitAfterItemGroupDestory() throws Exception {
		int null_amount_x = 0;
		for (Item[] items_y : items) {
			int null_amount_y = 0;
			for (int y = Scene.Y_LENGTH - 1; y >= 0; y--) {
				if (null == items_y[y]) {
					null_amount_y++;
				} else {
					items_y[y].moveTo(items_y[y].getX() - null_amount_x, items_y[y].getY() + null_amount_y);
				}
			}
			if (Scene.Y_LENGTH == null_amount_y) {
				null_amount_x++;
			}
		}

		initItemGroups();

	}
}
